
#include "stdafx.h"
#include <streams.h>
#include "tdirectshowgraph.h"

#define  WM_GRAPHNOTIFY  (WM_USER  + 1)

TDirectShowGraph::TDirectShowGraph()
{
    m_pGraph        = NULL;
    m_pMediaControl = NULL;
    m_pMediaEvent   = NULL;
    m_pBasicVideo   = NULL;
    m_pBasicAudio   = NULL;
    m_pVideoWindow  = NULL;
    m_pMediaSeeking = NULL;
}

TDirectShowGraph::~TDirectShowGraph()
{
    Release();
}

HRESULT TDirectShowGraph::Create(void)
{
    HRESULT hr = E_FAIL;
    if (!m_pGraph)
    {
        if (SUCCEEDED(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
            IID_IGraphBuilder, (void **)&m_pGraph)))
        {
            return QueryInterfaces();
        }
        m_pGraph = 0;
    }
    return S_OK;
}

HRESULT TDirectShowGraph::QueryInterfaces(void)
{
    HRESULT hr = E_FAIL;
    if (m_pGraph)
    {
        HRESULT hr = NOERROR;
        hr |= m_pGraph->QueryInterface(IID_IMediaControl, (void **)&m_pMediaControl);
        hr |= m_pGraph->QueryInterface(IID_IMediaEventEx, (void **)&m_pMediaEvent);
        hr |= m_pGraph->QueryInterface(IID_IBasicVideo, (void **)&m_pBasicVideo);
        hr |= m_pGraph->QueryInterface(IID_IBasicAudio, (void **)&m_pBasicAudio);
        hr |= m_pGraph->QueryInterface(IID_IVideoWindow, (void **)&m_pVideoWindow);
        hr |= m_pGraph->QueryInterface(IID_IMediaSeeking, (void **)&m_pMediaSeeking);
        if (m_pMediaSeeking)
        {
            m_pMediaSeeking->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME);
        }
        return hr;
    }
    return hr;
}

void TDirectShowGraph::Release(void)
{
    if (m_pMediaSeeking)
    {
        m_pMediaSeeking->Release();
        m_pMediaSeeking = NULL;
    }
    if (m_pMediaControl)
    {
        m_pMediaControl->Release();
        m_pMediaControl = NULL;
    }
    if (m_pMediaEvent)
    {
        m_pMediaEvent->Release();
        m_pMediaEvent = NULL;
    }
    if (m_pBasicVideo)
    {
        m_pBasicVideo->Release();
        m_pBasicVideo = NULL;
    }
    if (m_pBasicAudio)
    {
        m_pBasicAudio->Release();
        m_pBasicAudio = NULL;
    }
    if (m_pVideoWindow)
    {
        m_pVideoWindow->put_Visible(OAFALSE);
        m_pVideoWindow->put_MessageDrain((OAHWND)NULL);
        m_pVideoWindow->put_Owner(OAHWND(0));
        m_pVideoWindow->Release();
        m_pVideoWindow = NULL;
    }
    if (m_pGraph) 
    {
        m_pGraph->Release(); 
        m_pGraph = NULL;
    }
}

HRESULT TDirectShowGraph::Attach(IGraphBuilder * inGraphBuilder)
{
    Release();
    HRESULT hr = E_FAIL;
    if (inGraphBuilder)
    {
        inGraphBuilder->AddRef();
        m_pGraph = inGraphBuilder;
        hr = QueryInterfaces();
    }
    return hr;
}

IMediaEventEx * TDirectShowGraph::GetEventHandle(void)
{
    return m_pMediaEvent;
}

HRESULT TDirectShowGraph::AddFilter(IBaseFilter *pFilter)
{
    HRESULT hr = E_FAIL;
    if(m_pGraph)
        hr = m_pGraph->AddFilter(pFilter, NULL);
    return hr;
}

HRESULT TDirectShowGraph::SetDisplayWindow(HWND inWindow)
{	
    HRESULT hr = E_FAIL;
    if (m_pVideoWindow)
    {
        hr = m_pVideoWindow->put_Visible(OAFALSE);
        hr = m_pVideoWindow->put_Owner((OAHWND)inWindow);

        RECT windowRect;
        ::GetClientRect(inWindow, &windowRect);
        hr = ResizeVideoWindow(windowRect);
        
        hr = m_pVideoWindow->put_WindowStyle(WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS);
        hr = m_pVideoWindow->put_MessageDrain((OAHWND) inWindow);

        if (inWindow != NULL)
        {
            hr = m_pVideoWindow->put_Visible(OATRUE);
        }
        else
        {
            hr = m_pVideoWindow->put_Visible(OAFALSE);
        }
        return hr;
    }
    return hr;
}

HRESULT TDirectShowGraph::ResizeVideoWindow(RECT &windowRect)
{
    if (m_pVideoWindow)
    {
        long lVisible = OATRUE;
        m_pVideoWindow->get_Visible(&lVisible);
        m_pVideoWindow->put_Visible(OAFALSE);


        long nVWith = 0, nVHeight = 0;
        m_pBasicVideo->GetVideoSize(&nVWith, &nVHeight);
        if(nVHeight <= 0)
           return E_FAIL;
        if(nVWith <= 0)
           return E_FAIL;

        long nLeft = 0;
        long nRight = 0;
        long nTop = 0;
        long nBottom = 0;

        if(nVHeight * (windowRect.right - windowRect.left + 1) < (windowRect.bottom - windowRect.top + 1) * nVWith)
        {
            nLeft = windowRect.left;
            nRight = windowRect.right;
            long nHeighx = nVHeight * (windowRect.right - windowRect.left + 1) / nVWith;
            long mid = (windowRect.top + windowRect.bottom) / 2;
            nTop = mid - nHeighx / 2;
            nBottom = nTop + nHeighx;
        }
        else
        {
            nTop = windowRect.top;
            nBottom = windowRect.bottom;
            long nWithx = nVWith * (windowRect.bottom - windowRect.top + 1) / nVHeight;
            long mid = (windowRect.left + windowRect.right) / 2;
            nLeft = mid - nWithx / 2;
            nRight = nLeft + nWithx;
        }
        m_pVideoWindow->SetWindowPosition(nLeft, nTop, nRight - nLeft + 1, nBottom - nTop + 1);

        m_pVideoWindow->put_Visible(lVisible);
        return S_OK;
    }
    return E_FAIL;
}

HRESULT TDirectShowGraph::SetNotifyWindow(HWND inWindow)
{
    if (m_pMediaEvent)
    {
        return m_pMediaEvent->SetNotifyWindow((OAHWND)inWindow, WM_GRAPHNOTIFY, 0);
    }
    return E_FAIL;
}

LONG TDirectShowGraph::GetEventCode(WPARAM inWParam, LPARAM inLParam)
{
    if (m_pMediaEvent)
    {
        LONG eventCode = 0, eventParam1 = 0, eventParam2 = 0;
        while (SUCCEEDED(m_pMediaEvent->GetEvent(&eventCode, &eventParam1, &eventParam2, 0)))
        {
            m_pMediaEvent->FreeEventParams(eventCode, eventParam1, eventParam2);
            return eventCode;
        }
    }
    return 0;
}

HRESULT TDirectShowGraph::Run(void)
{
    if (m_pGraph && m_pMediaControl)
    {
        if (!IsRunning())
        {
            return m_pMediaControl->Run();
        }
        else
        {
            return S_OK;
        }
    }
    return E_FAIL;
}

HRESULT TDirectShowGraph::Stop(void)
{
    if (m_pGraph && m_pMediaControl)
    {
        if (!IsStopped())
        {	
            return m_pMediaControl->Stop();
        }
        else
        {
            return S_OK;
        }
    }
    return E_FAIL;
}

HRESULT TDirectShowGraph::Pause(void)
{
    if (m_pGraph && m_pMediaControl)
    {
        if (!IsPaused())
        {	
            return m_pMediaControl->Pause();

        }
        else
        {
            return S_OK;
        }
    }
    return E_FAIL;
}

bool TDirectShowGraph::IsRunning(void)
{
    if (m_pGraph && m_pMediaControl)
    {
        OAFilterState state = State_Stopped;
        if (SUCCEEDED(m_pMediaControl->GetState(10, &state)))
        {
            return state == State_Running;
        }
    }
    return false;
}

bool TDirectShowGraph::IsStopped(void)
{
    if (m_pGraph && m_pMediaControl)
    {
        OAFilterState state = State_Stopped;
        if (SUCCEEDED(m_pMediaControl->GetState(10, &state)))
        {
            return state == State_Stopped;
        }
    }
    return false;
}

bool TDirectShowGraph::IsPaused(void)
{
    if (m_pGraph && m_pMediaControl)
    {
        OAFilterState state = State_Stopped;
        if (SUCCEEDED(m_pMediaControl->GetState(10, &state)))
        {
            return state == State_Paused;
        }
    }
    return false;
}

HRESULT TDirectShowGraph::SetFullScreen(BOOL inEnabled)
{
    if (m_pVideoWindow)
    {
        return m_pVideoWindow->put_FullScreenMode(inEnabled ? OATRUE : OAFALSE);
    }
    return E_FAIL;
}

bool TDirectShowGraph::GetFullScreen(void)
{
    if (m_pVideoWindow)
    {
        long  fullScreenMode = OAFALSE;
        m_pVideoWindow->get_FullScreenMode(&fullScreenMode);
        return (fullScreenMode == OATRUE);
    }
    return false;
}

// IMediaSeeking features
HRESULT TDirectShowGraph::GetCurrentPosition(double * outPosition)
{
    if (m_pMediaSeeking)
    {
        __int64 position = 0;
        if (SUCCEEDED(m_pMediaSeeking->GetCurrentPosition(&position)))
        {
            *outPosition = ((double)position) / 10000000.;
            return S_OK;
        }
    }
    return E_FAIL;
}

HRESULT TDirectShowGraph::GetStopPosition(double * outPosition)
{
    if (m_pMediaSeeking)
    {
        __int64 position = 0;
        if (SUCCEEDED(m_pMediaSeeking->GetStopPosition(&position)))
        {
            *outPosition = ((double)position) / 10000000.;
            return S_OK;
        }
    }
    return E_FAIL;
}

HRESULT TDirectShowGraph::SetCurrentPosition(double inPosition)
{
    if (m_pMediaSeeking)
    {
        __int64 one = 10000000;
        __int64 position = (__int64)(one * inPosition);
        HRESULT hr = m_pMediaSeeking->SetPositions(&position, AM_SEEKING_AbsolutePositioning | AM_SEEKING_SeekToKeyFrame, 
            0, AM_SEEKING_NoPositioning);
        return hr;
    }
    return E_FAIL;
}

HRESULT TDirectShowGraph::SetStartStopPosition(double inStart, double inStop)
{
    if (m_pMediaSeeking)
    {
        __int64 one = 10000000;
        __int64 startPos = (__int64)(one * inStart);
        __int64 stopPos  = (__int64)(one * inStop);
        HRESULT hr = m_pMediaSeeking->SetPositions(&startPos, AM_SEEKING_AbsolutePositioning | AM_SEEKING_SeekToKeyFrame, 
            &stopPos, AM_SEEKING_AbsolutePositioning | AM_SEEKING_SeekToKeyFrame);
        return hr;
    }
    return E_FAIL;
}

HRESULT TDirectShowGraph::GetDuration(double * outDuration)
{
    if (m_pMediaSeeking)
    {
        __int64 length = 0;
        if (SUCCEEDED(m_pMediaSeeking->GetDuration(&length)))
        {
            *outDuration = ((double)length) / 10000000.;
            return S_OK;
        }
    }
    return E_FAIL;
}

HRESULT TDirectShowGraph::SetPlaybackRate(double inRate)
{
    if (m_pMediaSeeking)
    {
        return m_pMediaSeeking->SetRate(inRate);
    }
    return E_FAIL;
}

HRESULT TDirectShowGraph::IncreasePlaybackRate(bool bIncrease)
{
    if (m_pMediaSeeking)
    {
        static double rates[10] = {0.2, 0.5, 0.8, 1, 1.5, 2.0, 2.5, 3, 3.5, 4};
        double currentRate = 1.0;
        m_pMediaSeeking->GetRate(&currentRate);
        int l = 0, r = 9, mid = 0;
        int pos = 0;
        while(l <= r)
        {
            mid = (l + r) >> 1;
            if(currentRate <= rates[mid])
            {
                pos = mid;
                r = mid - 1;
            }
            else
            {
                l = mid + 1;
            }
        }
        bIncrease ? pos++ : pos--;
        if(pos < 0) pos = 0;
        if(pos > 9) pos = 9;
        HRESULT hr = m_pMediaSeeking->SetRate(rates[pos]);
        return hr;
    }
    return E_FAIL;
}

// Attention: range from -10000 to 0, and 0 is FULL_VOLUME.
HRESULT TDirectShowGraph::_SetAudioVolume(long inVolume)
{
    if (m_pBasicAudio)
    {
        return m_pBasicAudio->put_Volume(inVolume);
    }
    return E_FAIL;
}

long TDirectShowGraph::_GetAudioVolume(void)
{
    long volume = 0;
    if (m_pBasicAudio)
    {
        m_pBasicAudio->get_Volume(&volume);
    }
    return volume;
}

int TDirectShowGraph::GetAudioVolume()
{
    long volume = 0;
    if (m_pBasicAudio)
    {
        m_pBasicAudio->get_Volume(&volume);
        volume = volume / 100 + 100; 
    }
    return volume;
}

HRESULT TDirectShowGraph::ChangeAudioVolume(int nVolume)
{
    //ASSERT(nVolume >= 0 && nVolume <= 100);
    if (!m_pBasicAudio)
        return E_FAIL;

    if(nVolume < 0)
        nVolume = 0;
    if(nVolume > 100)
        nVolume = 100;
    m_nVolume = nVolume;

    long lVolume = (nVolume - 100) * 100;
    return m_pBasicAudio->put_Volume(lVolume);
}

HRESULT TDirectShowGraph::IncreaseVolume(int nVolume)
{
    HRESULT hr = E_FAIL;
    long volume = 0;
    if (m_pBasicAudio)
    {
        m_pBasicAudio->get_Volume(&volume);
        volume = volume / 100 + 100; 
        volume += nVolume;
        hr = ChangeAudioVolume(volume);
    }
    return hr;
}

HRESULT TDirectShowGraph::Mute()
{
    if (!m_pBasicAudio)
        return E_FAIL;

    return m_pBasicAudio->put_Volume(-10000);
}

HRESULT TDirectShowGraph::UnMute()
{
    if (!m_pBasicAudio)
        return E_FAIL;

    long lVolume = (m_nVolume - 100) * 100;
    return m_pBasicAudio->put_Volume(lVolume);
}

HRESULT TDirectShowGraph::_SetAudioBalance(long inBalance)
{
    if (m_pBasicAudio)
    {
        return m_pBasicAudio->put_Balance(inBalance);
    }
    return E_FAIL;
}

long TDirectShowGraph::_GetAudioBalance(void)
{
    long balance = 0;
    if (m_pBasicAudio)
    {
        m_pBasicAudio->get_Balance(&balance);
    }
    return balance;
}

HRESULT TDirectShowGraph::Render(IPin *pPin)
{
    HRESULT hr = E_FAIL;
    if (m_pGraph)
    {
        hr = m_pGraph->Render(pPin);
    }
    return hr;
}

